home *** CD-ROM | disk | FTP | other *** search
- #import "GlyphView.h"
- #import "MenuManager.h"
- #import "ClassManager.h"
- #import "Terminator.h"
- #import "Tray.h"
- #import "MenuManager.h"
- #import "InstanceManager.h"
- #import "GlyphManager.h"
- #import "NuPerformer.h"
- #import "MKManager.h"
- #import "FGWindow.h"
- #import <appkit/Application.h>
- #import <appkit/Window.h>
- #import <appkit/NXCursor.h>
- #import <appkit/NXImage.h>
- #import <appkit/NXBrowser.h>
- #import <appkit/TextField.h>
- #import <appkit/ButtonCell.h>
- #import <appkit/SavePanel.h>
- #import <defaults/defaults.h>
- #import <appkit/publicWraps.h>
- #import <musickit/Conductor.h>
- #import <musickit/Orchestra.h>
- #import <dpsclient/psops.h>
- #import <dpsclient/wraps.h>
- #import <c.h>
- #import <strings.h>
- #import <math.h>
-
- #define GLYPHVIEWVERSION 1
-
- extern id Nu ;
- static id glyphViewList ;
- const char * glyphTypes[] = {"m",NULL} ;
-
- void NuClearRect(NXRect * aRect)
- { // use this to clear rectangles
- PSsetgray(1.0) ;
- PSsetalpha(0.0) ;
- PSrectfill(aRect->origin.x,aRect->origin.y,
- aRect->size.width,aRect->size.height) ;
- PSsetgray(0.0) ;
- PSsetalpha(1.0) ;
- }
-
- @implementation GlyphView: View
- { @public
- id glyphMsgView, mouseObject ;
- id playButton, freezeButton, lockButton, resizeButton ;
- Glyph *rootGlyph ;
- struct glyphViewFlags flags ;
- char *fileName ;
- // music kit data
- double beatsPerMinute, headRoom, deltaT, samplingRate, tickPeriod ;
- BOOL fastResponse ;
- }
-
- + become: (List *) newIds if: (List *) oldIds views: viewList;
- { // if any existing glyphs are members of oldClass, then
- // they are "coerced" into newClass by changing their
- // "isa" pointers. viewList should be a List object:
- // I add the ids of all glyphViews containing Glyphs whose
- // class changed to this list..
- int viewNum, viewKnt ;
- id theView, theGlyph ;
- viewKnt = [glyphViewList count] ;
- for(viewNum = 0 ; viewNum < viewKnt ; viewNum++)
- { theView = [glyphViewList objectAt: viewNum] ;
- theGlyph = [theView rootGlyph] ;
- if([self glyph: theGlyph become: newIds if: oldIds])
- [viewList addObject: theView] ;
- }
- return self ;
- }
-
- + cursor ;
- { // draws and sets the Nutation cursor
- static id cursor = nil ;
- if(!cursor)
- { id anImage ;
- NXSize aSize = {16.0,16.0} ;
- NXPoint hotSpot = {7.0,7.0} ;
- anImage = [[NXImage alloc] initSize: &aSize] ;
- [anImage lockFocus] ;
- PSsendstring(
- "0 setalpha "
- "1 setgray "
- "0 0 16 16 rectfill "
- "1 setalpha "
- "0 setgray "
- "1 setlinewidth "
- "3 3 moveto "
- "6 6 lineto "
- "3 2 moveto "
- "5 4 lineto "
- "2 3 moveto "
- "4 5 lineto "
- "9 9 moveto "
- "12 12 lineto "
- "11 10 moveto "
- "13 12 lineto "
- "10 11 moveto "
- "12 13 lineto "
- "3 12 moveto "
- "5 10 lineto "
- "3 13 moveto "
- "4 12 lineto "
- "2 12 moveto "
- "3 11 lineto "
- "9 6 moveto "
- "11 4 lineto "
- "11 5 moveto "
- "12 4 lineto "
- "10 4 moveto "
- "11 3 lineto "
- "stroke ") ;
- PScvx() ;
- PSexec() ;
- [anImage unlockFocus] ;
- cursor = [[NXCursor alloc] initFromImage: anImage] ;
- [cursor setHotSpot: &hotSpot] ;
- }
- return cursor ;
- }
-
- // need this to get ahold of glyph's "private"
- // isa instance var
- struct glyphDef
- { @defs(Glyph)
- } ;
-
- + (BOOL) glyph: (Glyph *) aGlyph become: (List *) newList if: (List *) oldList ;
- { // if aGlyph->isa is in oldList, change it to
- // corresponding value on newList.
- // This should only be called on the root of a TTree.
- // It recurses through all Glyphs in the tree, and
- // returns YES iff at least one Glyph in the
- // tree "became" a value on newList.
- // Also descends the super_class chain to see if a super_class
- // needs to be replaced.
- BOOL rval = NO ;
- struct glyphDef *theGlyph ;
- struct objc_class *theClass ;
- int i, knt ;
- knt = [newList count] ;
- if(aGlyph->flags.isTerminator)
- return NO ;
- theGlyph = (struct glyphDef *) aGlyph ;
- for(i = 0 ; i < knt ; i++)
- { if(theGlyph->isa == (Class) [oldList objectAt: i])
- { theGlyph->isa = (Class) [newList objectAt: i] ;
- rval = YES ;
- break ;
- }
- }
- if(!rval) // search up superclass chain
- { theClass = theGlyph->isa ;
- while(theClass->super_class)
- { for(i = 0 ; i < knt ; i++)
- { if(theClass->super_class == (Class) [oldList objectAt: i])
- { theClass->super_class = (Class) [newList objectAt: i] ;
- rval = YES ;
- break ;
- }
- }
- if(rval)
- break ;
- theClass = theClass->super_class ;
- }
- }
- rval = [self glyph: aGlyph->is become: newList if: oldList] || rval ;
- rval = [self glyph: aGlyph->then become: newList if: oldList] || rval ;
- return rval ;
- }
-
- + glyphViewList ;
- { // return the list of glyph views
- return glyphViewList ;
- }
-
- + initialize ;
- { [self setVersion: GLYPHVIEWVERSION] ;
- glyphViewList = [[List alloc] init] ;
- return self ;
- }
-
- - (BOOL) acceptsFirstResponder ;
- { return YES ;
- }
-
- - browse: sender ;
- { // open a ClassManager on the targetGlyph
- // Keep this in glyphView
- char *aClass = [[[Nu targetGlyph] class] name] ;
- if(! [[Nu glyphManager] editFile: nil class: aClass])
- NXRunAlertPanel("Nu","Error: can't find class definition "
- "for class %s\n", NULL,NULL,NULL,aClass) ;
- return self ;
- }
-
- - clearGlyphMsgView: aView ;
- { if(glyphMsgView)
- [glyphMsgView setStringValue: ""] ;
- return self ;
- }
-
- -displayRaised: sender ;
- { return [rootGlyph displayRaised] ;
- }
-
- - drawSelf:(const NXRect *)rects :(int)rectCount ;
- { if(NXDrawingStatus == NX_PRINTING)
- return [rootGlyph printSelf: rects :rectCount] ;
- else
- return [rootGlyph drawSelf: rects :rectCount] ;
- }
-
- - fileName: (char *) fname ;
- { // copy fname into ivar, set the
- // window's title to fname, with the
- // filename followed by the path
- char fullName[128], *name ;
- if(fileName)
- free(fileName) ;
- fileName = (char *) malloc(strlen(fname) + 3) ;
- strcpy(fileName, fname) ;
- strcpy(fullName, fileName) ;
- name = rindex(fullName,'/') ;
- if(name != NULL) // is there is a path,
- { char title[129] ;
- name[0] = '\0' ; // split string into 2 pieces
- name++ ;
- sprintf(title,"%s %s",name,fullName) ;
- [window setTitle: title] ;
- }
- else // no path, just use the filename
- [window setTitle: fname] ;
- return self ;
- }
-
- - free ;
- { free(fileName) ;
- return [super free] ;
- }
-
- - freezeButton ;
- { return freezeButton ;
- }
-
- - freezeButton: sender ;
- { // freeze button pressed...
- // Just relay this to the rootGlyph
- [rootGlyph freezeButton: freezeButton] ;
- return self ;
- }
-
-
- - glyphMsg: (char *) aMsg ;
- { // flash the msg in my message view
- if(glyphMsgView)
- { [glyphMsgView setStringValue: aMsg] ;
- [self perform: @selector(clearGlyphMsgView:) with: self
- afterDelay: 1500 cancelPrevious: YES] ;
- }
- return self ;
- }
-
- - inspect: sender ;
- { // open an instance manager on target glyph
- // all the work is handled by the menuManager
- return [Nu instance: self] ;
- }
-
- - lock: sender ;
- { // relay this action message
- [rootGlyph lock] ;
- return self ;
- }
-
- - lockButton ;
- { // return id of the lock button
- return lockButton ;
- }
-
- - mouseDown: (NXEvent *) anEvent ;
- { // normally, mouseObject is set to
- // the rootGlyph, but it is set to
- // the inspectorManager during resizing.
- return [mouseObject mouseDown: anEvent] ;
- }
-
- - mouseDragged: (NXEvent *) anEvent ;
- { [window setDocEdited: YES] ;
- return [mouseObject mouseDragged: anEvent] ;
- }
-
- - mouseUp: (NXEvent *) anEvent ;
- { return [mouseObject mouseUp: anEvent] ;
- }
-
-
- - play:sender ;
- { return [rootGlyph play: playButton] ;
- }
-
- - resize: sender ;
- { // go into (or out of, i.e. toggle) resizing "mode" by changing
- // the mouseObject ivar to divert mouse events
- // to the instanceManager (or, if leaving the mode,
- // by redirecting mouse events to saved mouseObject)
- id instanceManager = [Nu instanceManager] ;
- if(!flags.resizing) // enter the mode
- { flags.resizing = YES ;
- if([rootGlyph targetGlyph] == rootGlyph)
- { // don't allow resizing or the rootGlyph
- [resizeButton performClick: self] ;
- return self ;
- }
- mouseObject = instanceManager ;
- [self setCursor: [[instanceManager class] cursor]] ;
- [instanceManager startSizer: self] ;
- }
- else // exit the mode--replace mouseObject with targetGlyph
- { [instanceManager stopSizer: self] ;
- [self setCursor: [[self class] cursor]] ;
- flags.resizing = NO ;
- mouseObject = rootGlyph ;
- }
- return self ;
- }
-
-
- - resizeButton ;
- { // return id of resizeButton
- return resizeButton ;
- }
-
-
-
- - revert: sender ;
- { int rval ;
- // if in the glyph borwser, or if not edited, just return
- if(!flags.freeWhenClosed || ![window isDocEdited])
- return self ;
- rval = NXRunAlertPanel(
- "Revert","Window has unsaved changes. Revert?",
- "Revert","Don't Revert", NULL,
- fileName ? fileName: "Untitled") ;
- if (rval == NX_ALERTDEFAULT) // then revert...
- { if(fileName)
- { // put fileName into NULL terminated list,
- // get Menu Manager to open it from disk
- char *fileNames[2] ;
- fileNames[0] = fileName ;
- fileNames[1] = NULL ;
- [Nu openList: fileNames path: NULL] ;
- }
- else // file was never named, hence was brand new...
- [Nu newGlyphWindow: self] ;
- // now schedule my own demise
- [window setDocEdited: NO] ;
- [window perform: @selector(performClose:)
- with: self afterDelay:1 cancelPrevious: YES] ;
- }
- // else don't revert
- return self ;
- }
-
-
- - rightMouseDown: (NXEvent *) anEvent ;
- { return [mouseObject rightMouseDown: anEvent] ;
- }
-
- - rightMouseDragged: (NXEvent *) anEvent ;
- { return [mouseObject rightMouseDragged: anEvent] ;
- }
-
-
- - rightMouseUp: (NXEvent *) anEvent ;
- { return [mouseObject rightMouseUp: anEvent] ;
- }
-
- - rootGlyph ;
- { return rootGlyph ;
- }
-
- - rootGlyph: aGlyph ;
- { rootGlyph = aGlyph ;
- return self ;
- }
-
- -(BOOL) save:sender ;
- { if(fileName == NULL)
- return [self saveAs: sender] ;
- else
- return [self saveToFile] ;
- return YES ;
- }
-
- -(BOOL) saveAs:sender ;
- { id savePanel = [SavePanel new] ;
- [savePanel setRequiredFileType: "Nu"] ;
- if([savePanel runModalForDirectory: NULL file: fileName])
- { [self fileName: (char *) [savePanel filename]] ;
- return [self saveToFile] ;
- }
- else
- return NO ;
- }
-
-
- -(BOOL) saveToFile ;
- { // save my window and all its subviews and
- // subglyphs to fileName
- NXStream *aStream ;
- if(aStream = NXOpenTypedStreamForFile(fileName, NX_WRITEONLY))
- { // make rootGlyph the target before saving
- id targetGlyph = [rootGlyph targetGlyph] ;
- [rootGlyph becomeTarget] ;
- NXWriteRootObject(aStream,window) ;
- NXCloseTypedStream(aStream) ;
- [targetGlyph becomeTarget] ;
- [window setDocEdited: NO] ;
- return YES ;
- }
- return NO ;
- }
-
-
- - setCursor: aCursor ;
- { // make aCursor the cursor to use when mouse
- // is in this view
- [[superview superview] setDocCursor: aCursor] ;
- [window resetCursorRects] ;
- return self ;
- }
-
-
- /**
- ** music kit manager "set" methods... these methods are
- ** called by the manager on user interaction...they keep
- ** the glyphviews ivars in sync with the manager
- **/
-
- - setMKMBeatsPerMinute: (double) bPM ;
- { beatsPerMinute = bPM ;
- return self ;
- }
-
- - setMKMDeltaT: (double) seconds ;
- { deltaT = seconds ;
- return self ;
- }
-
- - setMKMFastResponse: (BOOL) YESorNO ;
- { fastResponse = YESorNO ;
- return self ;
- }
-
- - setMKMHeadRoom: (double) seconds ;
- { headRoom = seconds ;
- return self ;
- }
-
- - setMKMTickPeriod: (double) tP ;
- { tickPeriod = tP ;
- return self ;
- }
-
- - setMKMSamplingRate: (double) sR ;
- { samplingRate = sR ;
- return self ;
- }
-
- /*********/
-
- - setFreeWhenClosed: (BOOL) YESorNO ;
- { // set whether I free myself and glyphs when
- // my window closes. default is YES ;
- flags.freeWhenClosed = YESorNO ;
- return self ;
- }
-
- - setGlyphMsgView: anObject ;
- { // by making this method explicit, the GlyphManager
- // can reset the glyphMsgView to its own msgView.
- glyphMsgView = anObject ;
- [Nu glyphView: self] ;
- [self setupNew] ;
- return self ;
- }
-
-
- - setupArchived
- { // perform setup after unarchived
- [rootGlyph setup] ;
- return [self setupCommon] ;
- }
-
- - setupCommon ;
- { // perform setup common to both new and
- // unarchived glyphViews
- id mKM ;
- [[GlyphView glyphViewList] addObject: self] ;
- [window useOptimizedDrawing: YES] ;
- [window setDocEdited: NO] ;
- [window display] ;
- [self setCursor: [[self class] cursor]] ;
- [rootGlyph display: &bounds from: nil] ;
- [window setMiniwindowIcon: "gwIcon.tiff"] ;
- // get the music kit values, using what
- // is currently displayed in the manager's window
- mKM = [Nu musicKitManager] ;
- beatsPerMinute = [mKM beatsPerMinute] ;
- headRoom = [mKM headRoom] ;
- deltaT = [mKM deltaT] ;
- samplingRate = [mKM samplingRate] ;
- tickPeriod = [mKM tickPeriod] ;
- fastResponse = [mKM fastResponse] ;
- return self ;
- }
-
- - setupNew ;
- { // perform initial setup of a
- // brand new window
- id clipView = superview ;
- flags.freeWhenClosed = YES ;
- [window addToEventMask: NX_LMOUSEDOWNMASK | NX_RMOUSEDOWNMASK
- | NX_LMOUSEDRAGGEDMASK | NX_RMOUSEDRAGGEDMASK
- | NX_LMOUSEUPMASK | NX_RMOUSEUPMASK] ;
- // NOTE: this must come BEFORE you alloc init:: the rootGlyph,
- // as the rootGlyph must be able to get my id (via Nu).
- [Nu glyphView: self] ;
- // setup the rootGlyph and the mouseObject
- rootGlyph = mouseObject =
- [[objc_getClass("Root") alloc] init:frame.size.width
- :frame.size.height] ;
- // make sure scrollview autosizes its subviews
- [[clipView superview] setAutoresizeSubviews: YES] ;
- [clipView setAutosizing: NX_HEIGHTSIZABLE | NX_WIDTHSIZABLE] ;
- [clipView setAutoresizeSubviews: YES] ;
- return [self setupCommon] ;
- }
-
-
- - sizeTo: (float) width :(float) height ;
- { // glyphViews can only grow, they are not
- // allowed to shrink
- NXSize newSize ;
- id clipView = superview ;
- [super sizeTo: newSize.width = MAX(bounds.size.width,width)
- : newSize.height = MAX(bounds.size.height,height)] ;
- [rootGlyph sizeTo: newSize.width :newSize.height] ;
- [[clipView superview] reflectScroll: clipView] ;
- return self ;
- }
-
- - targetGlyph ;
- { return [rootGlyph targetGlyph] ;
- }
-
- - (int) tag ;
- { return GLYPHVIEWTAG ;
- }
-
-
- - updateMKManager ;
- { // copy my music kit data into the MKManager window
- id m ;
- m = [Nu musicKitManager] ;
- [m beatsPerMinute: beatsPerMinute] ;
- [m headRoom: headRoom] ;
- [m deltaT: deltaT] ;
- [m samplingRate: samplingRate] ;
- [m tickPeriod: tickPeriod] ;
- [m fastResponse: fastResponse] ;
- return self ;
- }
-
- - windowDidBecomeMain: sender ;
- { // inform Nu that I have become the main glyphView
- [Nu glyphView: self] ;
- // update the music kit defaults
- [self updateMKManager] ;
- return self ;
- }
-
- - windowDidResignMain: sender ;
- { return self ;
- }
-
-
- - windowWillClose: sender ;
- { // our window is about to close; need to free up
- // stuff we created if window will be freed
-
- if(flags.freeWhenClosed)
- { // in the glyph browser's glyphView, flags.freeWhenClosed = NO,
- // i.e. we never free it
- if([window isDocEdited])
- { int rval = NXRunAlertPanel(
- "Close","Save changes to %s?",
- "Save","Don't Save","Don't Close",
- fileName ? fileName: "Untitled") ;
- if (rval == NX_ALERTOTHER)
- return NO ;
- else if (rval == NX_ALERTDEFAULT)
- [self save: self] ;
- // else just close
- }
- if([Nu inspecting])
- if([[Nu instanceManager] targetGlyph] == [rootGlyph targetGlyph])
- [[Nu instanceManager] orderOut: self] ;
- // clear any delayed message
- // Historical note: in 2.0, afterDelay: 0 did the job.
- // In 3.0, this didn't work, but -1 did. Go figure.
- [self perform: @selector(clearGlyphMsgView:) with: self
- afterDelay: -1 cancelPrevious: YES] ;
- [rootGlyph basicFree] ;
- [[GlyphView glyphViewList] removeObject: self] ;
- // set current glyphView to that of the glyphManager
- [Nu glyphView: [[Nu glyphManager] glyphView]] ;
- }
- return self ;
- }
-
- -read: (NXTypedStream *) stream ;
- { int version ;
- version = NXTypedStreamClassVersion(stream,[[self class] name]) ;
- [super read: stream] ;
- [Nu glyphView: self] ;
- glyphMsgView = NXReadObject(stream) ;
- freezeButton = NXReadObject(stream) ;
- resizeButton = NXReadObject(stream) ;
- lockButton = NXReadObject(stream) ;
- playButton = NXReadObject(stream) ;
- rootGlyph = mouseObject = NXReadObject(stream) ;
- NXReadType(stream,"s",(void *) &flags) ;
- // unarchive music kit parameters
- NXReadTypes(stream,"fffffc",&beatsPerMinute,&headRoom,&deltaT,
- &samplingRate,&tickPeriod,&fastResponse) ;
- [self updateMKManager] ;
- return self ;
- }
-
- -write: (NXTypedStream *) stream ;
- { [super write: stream] ;
- NXWriteObjectReference(stream, glyphMsgView) ;
- NXWriteObjectReference(stream, freezeButton) ;
- NXWriteObjectReference(stream, resizeButton) ;
- NXWriteObjectReference(stream, lockButton) ;
- NXWriteObjectReference(stream, playButton) ;
- NXWriteObject(stream, rootGlyph) ;
- NXWriteType(stream,"s",(void *) &flags) ;
- NXWriteTypes(stream,"fffffc",&beatsPerMinute,&headRoom,&deltaT,
- &samplingRate,&tickPeriod,&fastResponse) ;
- return self ;
- }
-
- @end
-
-
-